core: Add ostree_commit_get_object_sizes API
authorDan Nicholson <nicholson@endlessm.com>
Tue, 21 Jan 2020 02:54:00 +0000 (19:54 -0700)
committerDan Nicholson <nicholson@endlessm.com>
Tue, 21 Jan 2020 03:46:29 +0000 (20:46 -0700)
This function parses the object listing in the `ostree.sizes` metadata
and returns an array of `OstreeCommitSizesEntry` structures.

Unfortunately, for reasons I don't understand, the linker wants to
resolve `_ostree_read_varuint64` from `ostree-core.c` even though it's
not used by `test-checksum.c` at all.

Makefile-tests.am
apidoc/ostree-sections.txt
src/libostree/libostree-devel.sym
src/libostree/ostree-core-private.h
src/libostree/ostree-core.c
src/libostree/ostree-core.h
src/libostree/ostree-repo-private.h

index fc2f2d910dba4fdd3ae17c4d606ef3556d44c978..a4acb8e0d8368dc37b6c8c7ab3eb32c36dfb70a2 100644 (file)
@@ -347,7 +347,10 @@ tests_test_varint_LDADD = $(TESTS_LDADD)
 tests_test_bsdiff_CFLAGS = $(TESTS_CFLAGS)
 tests_test_bsdiff_LDADD = libbsdiff.la $(TESTS_LDADD)
 
-tests_test_checksum_SOURCES = src/libostree/ostree-core.c tests/test-checksum.c
+tests_test_checksum_SOURCES = \
+       src/libostree/ostree-core.c \
+       src/libostree/ostree-varint.c \
+       tests/test-checksum.c
 tests_test_checksum_CFLAGS = $(TESTS_CFLAGS) $(libglnx_cflags)
 tests_test_checksum_LDADD = $(TESTS_LDADD)
 
index 33035ca742d91aef64d719cd56babd37036344a2..32cf5228a50d1e9d4fae4ede77e610166b6554c1 100644 (file)
@@ -151,6 +151,7 @@ ostree_validate_structureof_dirmeta
 ostree_commit_get_parent
 ostree_commit_get_timestamp
 ostree_commit_get_content_checksum
+ostree_commit_get_object_sizes
 OstreeCommitSizesEntry
 ostree_commit_sizes_entry_new
 ostree_commit_sizes_entry_copy
index 8e7473c596179969321028ffcb7113fdb1cc07ce..ff5f52c4ab667c989c4eec466408bc8b4ae15ee1 100644 (file)
@@ -20,6 +20,7 @@
 /* Add new symbols here.  Release commits should copy this section into -released.sym. */
 LIBOSTREE_2019.7 {
 global:
+  ostree_commit_get_object_sizes;
   ostree_commit_sizes_entry_copy;
   ostree_commit_sizes_entry_free;
   ostree_commit_sizes_entry_get_type;
index 43cf22c4ddfaa55ac20e1c7f4d848a83f77c625f..c1a82386931e82ad435fad0f8ad70ea890d3971c 100644 (file)
@@ -102,6 +102,9 @@ _ostree_checksum_inplace_from_bytes_v (GVariant *csum_v, char *buf)
  */
 #define _OSTREE_LOOSE_PATH_MAX (256)
 
+/* GVariant format for ostree.sizes metadata entries. */
+#define _OSTREE_OBJECT_SIZES_ENTRY_SIGNATURE "ay"
+
 char *
 _ostree_get_relative_object_path (const char        *checksum,
                                   OstreeObjectType   type,
index 160f954fb0797413821939d771be70e2b687d281..4667dd8f327d0f7ec5cb909ba0a350e36b703f5d 100644 (file)
@@ -32,6 +32,7 @@
 #include "ostree.h"
 #include "ostree-core-private.h"
 #include "ostree-chain-input-stream.h"
+#include "ostree-varint.h"
 #include "otutil.h"
 
 /* Generic ABI checks */
@@ -2500,6 +2501,117 @@ ostree_commit_sizes_entry_free (OstreeCommitSizesEntry *entry)
   g_free (entry);
 }
 
+static gboolean
+read_sizes_entry (GVariant                *entry,
+                  OstreeCommitSizesEntry **out_sizes,
+                  GError                 **error)
+{
+  gsize entry_size = g_variant_get_size (entry);
+  g_return_val_if_fail (entry_size >= OSTREE_SHA256_DIGEST_LEN + 2, FALSE);
+
+  const guchar *buffer = g_variant_get_data (entry);
+  if (buffer == NULL)
+    return glnx_throw (error, "Could not read ostree.sizes metadata entry");
+
+  char checksum[OSTREE_SHA256_STRING_LEN + 1];
+  ostree_checksum_inplace_from_bytes (buffer, checksum);
+  buffer += OSTREE_SHA256_DIGEST_LEN;
+  entry_size -= OSTREE_SHA256_DIGEST_LEN;
+
+  gsize bytes_read = 0;
+  guint64 archived = 0;
+  if (!_ostree_read_varuint64 (buffer, entry_size, &archived, &bytes_read))
+    return glnx_throw (error, "Unexpected EOF reading ostree.sizes varint");
+  buffer += bytes_read;
+  entry_size -= bytes_read;
+
+  guint64 unpacked = 0;
+  if (!_ostree_read_varuint64 (buffer, entry_size, &unpacked, &bytes_read))
+    return glnx_throw (error, "Unexpected EOF reading ostree.sizes varint");
+  buffer += bytes_read;
+  entry_size -= bytes_read;
+
+  /* On newer commits, an additional byte is used for the object type. */
+  OstreeObjectType objtype;
+  if (entry_size > 0)
+    {
+      objtype = *buffer;
+      if (objtype < OSTREE_OBJECT_TYPE_FILE || objtype > OSTREE_OBJECT_TYPE_LAST)
+        return glnx_throw (error, "Unexpected ostree.sizes object type %u",
+                           objtype);
+      buffer++;
+      entry_size--;
+    }
+  else
+    {
+      /* Assume the object is a file. */
+      objtype = OSTREE_OBJECT_TYPE_FILE;
+    }
+
+  g_autoptr(OstreeCommitSizesEntry) sizes = ostree_commit_sizes_entry_new (checksum,
+                                                                           objtype,
+                                                                           unpacked,
+                                                                           archived);
+
+  if (out_sizes != NULL)
+    *out_sizes = g_steal_pointer (&sizes);
+
+  return TRUE;
+}
+
+/**
+ * ostree_commit_get_object_sizes:
+ * @commit_variant: (not nullable): variant of type %OSTREE_OBJECT_TYPE_COMMIT
+ * @out_sizes_entries: (out) (element-type OstreeCommitSizesEntry) (transfer container) (optional):
+ *   return location for an array of object size entries
+ * @error: Error
+ *
+ * Reads a commit's "ostree.sizes" metadata and returns an array of
+ * #OstreeCommitSizesEntry in @out_sizes_entries. Each element
+ * represents an object in the commit. If the commit does not contain
+ * the "ostree.sizes" metadata, a %G_IO_ERROR_NOT_FOUND error will be
+ * returned.
+ *
+ * Since: 2019.7
+ */
+gboolean
+ostree_commit_get_object_sizes (GVariant   *commit_variant,
+                                GPtrArray **out_sizes_entries,
+                                GError    **error)
+{
+  g_return_val_if_fail (commit_variant != NULL, FALSE);
+
+  g_autoptr(GVariant) metadata = g_variant_get_child_value (commit_variant, 0);
+  g_autoptr(GVariant) sizes_variant =
+    g_variant_lookup_value (metadata, "ostree.sizes",
+                            G_VARIANT_TYPE ("a" _OSTREE_OBJECT_SIZES_ENTRY_SIGNATURE));
+  if (sizes_variant == NULL)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                   "No metadata key ostree.sizes in commit");
+      return FALSE;
+    }
+
+  g_autoptr(GPtrArray) sizes_entries =
+    g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_commit_sizes_entry_free);
+  g_autoptr(GVariant) entry = NULL;
+  GVariantIter entry_iter;
+  g_variant_iter_init (&entry_iter, sizes_variant);
+  while ((entry = g_variant_iter_next_value (&entry_iter)))
+    {
+      OstreeCommitSizesEntry *sizes_entry = NULL;
+      if (!read_sizes_entry (entry, &sizes_entry, error))
+        return FALSE;
+      g_clear_pointer (&entry, g_variant_unref);
+      g_ptr_array_add (sizes_entries, sizes_entry);
+    }
+
+  if (out_sizes_entries != NULL)
+    *out_sizes_entries = g_steal_pointer (&sizes_entries);
+
+  return TRUE;
+}
+
 /* Used in pull/deploy to validate we're not being downgraded */
 gboolean
 _ostree_compare_timestamps (const char   *current_rev,
index a61ae06c05ab8e6620643a5036e3e5cce57fe14d..106011235df42adf8bb5f95a861e166f51bf6800 100644 (file)
@@ -553,6 +553,11 @@ OstreeCommitSizesEntry *ostree_commit_sizes_entry_copy (const OstreeCommitSizesE
 _OSTREE_PUBLIC
 void                    ostree_commit_sizes_entry_free (OstreeCommitSizesEntry *entry);
 
+_OSTREE_PUBLIC
+gboolean ostree_commit_get_object_sizes (GVariant   *commit_variant,
+                                         GPtrArray **out_sizes_entries,
+                                         GError    **error);
+
 _OSTREE_PUBLIC
 gboolean ostree_check_version (guint required_year, guint required_release);
 
index 2864d81ea3fcd8c9fa007bfc6763c1bd7393bc72..0465327cb98559e9bb49f9b940496fa7d41ca0c3 100644 (file)
@@ -31,8 +31,6 @@ G_BEGIN_DECLS
 
 #define OSTREE_DELTAPART_VERSION (0)
 
-#define _OSTREE_OBJECT_SIZES_ENTRY_SIGNATURE "ay"
-
 #define _OSTREE_SUMMARY_CACHE_DIR "summaries"
 #define _OSTREE_CACHE_DIR "cache"